const { setSocket } = require("../utils/msgSave");
const ModbusCommunicationManager = require("./modbusCommunication");
let {
  startPollingAndWriting,
  writeProData,
  modbusStatusHandle,
  getWritingConfig,
  getPlcStatus,
  getCameraCount,
} = require("./modbusBuness");
let aryModbusStatusList = [];
const path = require("path");
const {
  initIp,
  getIps,
  ipForClient,
  changeIpStatus,
} = require("../utils/handleIp");

// 日志函数，支持 info/warn/error 级别，带时间戳
function log(level, message, ...optionalParams) {
  const timestamp = new Date().toISOString();
  if (optionalParams.length > 0) {
    console[level](
      `[${timestamp}] [${level.toUpperCase()}] ${message}`,
      ...optionalParams
    );
  } else {
    console[level](`[${timestamp}] [${level.toUpperCase()}] ${message}`);
  }
}

// 全局变量，用于保存当前连接的 Modbus 客户端实例
let plcClient;

// 用于前端通知的标题和事件名
const title = "机器人信息";
const eventName = "plcClientHandle";

// 生成一个 30 位的 "0" 字符串，用于占位或初始化
let snZero = "";
for (let i = 0; i < 30; i++) {
  snZero += "0";
}

// 定时器相关配置
let intervalId;

// 保存当前 WebSocket 连接对象，用于主动推送状态
let currentSocket = null;

// 存储定时器引用
let checkModbusStatusTimer = null;

// 添加一个变量来保存原始的socket实例
let globalSocketInstance = null;

/**
 * WebSocket 连接后，使用 modbus 协议连接 PLC 客户端
 * @param {*} socketInstance - WebSocket 实例
 */
async function modBusSocketInfo(socketInstance) {
  if (!socketInstance) {
    log("error", "无法建立连接：socketInstance 为空");
    return;
  }

  // 保存socket实例供全局使用
  globalSocketInstance = socketInstance;

  // websocket 建立链接
  socketInstance.on("connection", async (socket) => {
    intervalId = null;

    // 如果已有连接，先销毁
    disconnectPlcClient();

    try {
      await getModBusList(socket);
    } catch (error) {
      log("error", "获取ModBus列表失败:", error);
    }

    // WebSocket 断开连接时清理资源
    socket.on("disconnect", handleWebSocketDisconnect);

    // 监听信息
    socket.on("message", handleMessage);
  });
}

/**
 * 断开机器人连接
 */
function disconnectPlcClient() {
  if (plcClient && plcClient.isConnected) {
    try {
      plcClient.disconnect();
    } catch (err) {
      log("error", "断开现有连接时出错:", err.message);
    }
  }
}

/**
 * 处理WebSocket断开连接
 */
function handleWebSocketDisconnect() {
  log("warn", "plcClient modbus WebSocket连接断开");
  disconnectPlcClient();

  if (intervalId) {
    clearInterval(intervalId);
    intervalId = null;
  }

  currentSocket = null;
}

/**
 * 处理接收到的消息
 */
async function handleMessage(res) {
  let data;
  if (typeof res === "string") {
    data = JSON.parse(res);
  } else {
    data = res;
  }
  // 更改机器人状态
  if (data.type == "changeState") {
    // 判断是否是可更改
    const {
      robotStatus,
      robotPosition,
      robotPhotoStatus,
      robotAllFinished,
      robotOneFinished,
    } = modbusStatusHandle(index);
    // 如果机器人在运行状态
    if (robotStatus == 1) {
      const wirtToPlcData = await getWritingConfig();
      wirtToPlcData[index].splice(20, 1, data.data);
    } else {
      // 构造连接成功消息
      const content = `机器人状态处于非运行状态，无法发送`;
      await setSocket(this, title, content, "", "", eventName);
    }
  }
}

/**
 * 获取 PLC 设备列表
 * @param {*} socket - 当前连接的 WebSocket
 */
async function getModBusList(socket) {
  try {
    const { count, rows } = await getIps(2);
    if (count === 0) {
      log("warn", "未找到PLC设备");
      return;
    }

    const flagInitIp = await initIp(2);
    if (!flagInitIp) {
      log("error", "初始化IP失败");
      return;
    }

    const ipListForPlcClient = await ipForClient(count, rows);

    if (ipListForPlcClient.length === 0) {
      const content = "机器人,没有找到";
      await setSocket(
        socket,
        title,
        content,
        "",
        "没有找到plc 设备列表",
        eventName
      );
    } else {
      log("info", "准备对机器人进行连接 ");
      await plcConnect(socket, ipListForPlcClient);
    }
  } catch (error) {
    log("error", "获取ModBus列表时发生错误:", error);
  }
}

/**
 * 连接指定的 PLC 客户端
 * @param {*} socket - 当前 WebSocket
 * @param {*} ipListForPlcClient - PLC 设备列表
 */
async function plcConnect(socket, ipListForPlcClient) {
  if (!ipListForPlcClient || ipListForPlcClient.length === 0) {
    log("error", "PLC设备列表为空");
    return;
  }

  const item = ipListForPlcClient[0];

  if (!item || !item.ip || !item.port) {
    log("error", "PLC设备信息不完整");
    return;
  }

  // 创建 Modbus 客户端实例
  plcClient = new ModbusCommunicationManager(item.ip, item.port);

  // 注册事件监听
  registerClientEvents(socket, item, plcClient);

  try {
    // 建立 Modbus 连接
    await plcClient.connect();
    if (!plcClient.isConnected) {
      log("error", "连接建立失败");
      return;
    }

    // 更新数据库中的连接状态为"已连接"
    await changeIpStatus({
      id: item.id,
      status: "1",
      ip: item.ip,
      port: item.port,
      type: item.type,
      beUsed: item.beUsed,
    });

    // 构造连接成功消息
    const content = `ip是${item.ip},端口是${item.port}的机器人,连接成功`;

    // payload 是发送给前端的数据结构，包含详细信息
    const payload = JSON.stringify({
      id: item.id,
      status: "1",
      ip: item.ip,
      port: item.port,
      type: item.type,
      beUsed: item.beUsed,
    });

    // 通过 WebSocket 推送连接成功消息
    await setSocket(socket, title, content, item.ip, payload, eventName);

    log("info", "modBus进行连接成功====");

    // 保存当前 socket 实例，用于后续推送
    currentSocket = socket;
    4;
    // 启动轮询和写入操作
    startPollingAndWriting(socket, plcClient);
  } catch (error) {
    // log("error", "连接失败:", error.message);

    // 构造连接成功消息
    const content = `ip是${item.ip},端口是${item.port}的机器人,连接失败`;

    // payload 是发送给前端的数据结构，包含详细信息
    const payload = JSON.stringify({
      id: item.id,
      status: "1",
      ip: item.ip,
      port: item.port,
      type: item.type,
      beUsed: item.beUsed,
    });

    // 通过 WebSocket 推送连接成功消息
    await setSocket(socket, title, content, item.ip, payload, eventName);
    await handleConnectionFailure(item, socket);
  }
}

/**
 * 处理连接失败的情况
 * @param {*} item - PLC设备信息
 * @param {*} socket - WebSocket实例
 */
async function handleConnectionFailure(item, socket) {
  // 更新数据库为"连接失败"
  try {
    await changeIpStatus({
      id: item.id,
      status: "0",
      ip: item.ip,
      port: item.port,
      type: item.type,
      beUsed: item.beUsed,
    });
  } catch (dbError) {
    log("error", "更新数据库状态失败:", dbError);
  }

  // 构造连接失败消息
  const content = `${item.ip},端口${item.port}的机器人,连接失败~~~`;

  // 通过 WebSocket 推送连接失败消息
  await setSocket(socket, title, content, item.ip, "connectError", eventName);
}

/**
 * 注册 Modbus 客户端事件监听
 * @param {*} socket - WebSocket 实例
 * @param {*} item - 当前连接的设备信息
 * @param {*} client - Modbus 客户端实例
 */
async function registerClientEvents(socket, item, client) {
  if (!client) {
    log("error", "无法注册事件监听器：客户端实例为空");

    // 构造连接失败消息
    const content = `无法注册事件监听器：客户端实例为空`;

    // 通过 WebSocket 推送连接失败消息
    await setSocket(socket, title, content, item.ip, "connectError", eventName);

    return;
  }

  client.on("error", (err) => handleClientError(socket, item, "error", err));
  client.on("disconnect", () => {
    handleClientDisconnect(socket, item);
  });
  client.on("connectFailed", (err) =>
    handleClientConnectFailed(socket, item, err)
  );
  client.on("timeout", () => handleClientTimeout(socket, item));

  // 监听重连成功事件
  client.on("connect", () => {
    handleClientReconnect(socket, item);
  });
}

/**
 * 处理客户端重连成功
 * @param {*} socket - WebSocket实例
 * @param {*} item - PLC设备信息
 */
async function handleClientReconnect(socket, item) {
  log("info", `客户端 ${item.ip}:${item.port} 重新连接成功`);

  // 更新数据库状态
  changeIpStatus({
    id: item.id,
    status: "1",
    ip: item.ip,
    port: item.port,
    type: item.type,
    beUsed: item.beUsed,
  }).catch((err) => {
    log("error", "更新数据库连接状态失败:", err.message);
  });

  // 发送连接成功消息到前端
  const content = `${item.ip},端口${item.port}的机器人,重新连接成功`;
  const payload = JSON.stringify({
    id: item.id,
    status: "1",
    ip: item.ip,
    port: item.port,
    type: item.type,
    beUsed: item.beUsed,
  });

  if (currentSocket && currentSocket.connected) {
    setSocket(currentSocket, title, content, item.ip, payload, eventName).catch(
      async (err) => {
        log("error", "发送连接成功消息失败:", err.message);
        // 构造连接失败消息
        const content = `发送连接成功消息失败:${err.message}`;

        // 通过 WebSocket 推送连接失败消息
        await setSocket(
          socket,
          title,
          content,
          item.ip,
          "connectError",
          eventName
        );
      }
    );
  }

  // 添加特定的业务处理逻辑 - 网线重新连接后的处理
  handleNetworkReconnection(socket, item, plcClient);

  // 添加延迟再启动轮询，确保PLC完全准备好
  setTimeout(() => {
    if (currentSocket && currentSocket.connected && plcClient.isConnected) {
      // 重新启动轮询
      startPollingAndWriting(currentSocket, plcClient);
    }
  }, 5000);
}

/**
 * 处理 Modbus 客户端连接错误事件
 */
function handleClientError(socket, item, type, err) {
  log("error", `PLC 客户端连接错误: ${err.message}`);
  if (currentSocket && currentSocket.connected) {
    currentSocket.emit("plcStatus", {
      status: "error",
      ip: item.ip,
      port: item.port,
      message: err.message,
      timestamp: new Date().toISOString(),
    });
  }
  const content = `${item.ip}:${item.port} 机器人连接失败（${type}事件）`;
  setSocket(socket, title, content, item.ip, type, eventName).catch((err) => {
    log("error", "发送socket消息失败:", err);
  });
}

/**
 * 处理 Modbus 客户端断开连接事件
 */
function handleClientDisconnect(socket, item) {
  log("warn", `PLC 客户端断开连接`);

  // 通知前端掉线并将状态置为0
  handleNetworkDisconnectionNotification(item);

  if (currentSocket && currentSocket.connected) {
    currentSocket.emit("plcStatus", {
      status: "disconnected",
      ip: item.ip,
      port: item.port,
      timestamp: new Date().toISOString(),
    });
  }
  const content = `${item.ip}:${item.port} 机器人断开连接`;
  setSocket(socket, title, content, item.ip, "disconnect", eventName).catch(
    (err) => {
      log("error", "发送socket消息失败:", err);
    }
  );
}

/**
 * 处理 Modbus 客户端连接失败事件
 */
function handleClientConnectFailed(socket, item, err) {
  log("error", `PLC 客户端连接失败: ${err.message}`);

  // 通知前端掉线并将状态置为0
  handleNetworkDisconnectionNotification(item);

  if (currentSocket && currentSocket.connected) {
    currentSocket.emit("plcStatus", {
      status: "connectFailed",
      ip: item.ip,
      port: item.port,
      message: err.message,
      timestamp: new Date().toISOString(),
    });
  }
  const content = `${item.ip}:${item.port} 机器人连接失败（connectFailed事件）`;
  setSocket(socket, title, content, item.ip, "connectFailed", eventName).catch(
    (err) => {
      log("error", "发送socket消息失败:", err);
    }
  );
}

/**
 * 处理 Modbus 请求超时事件
 */
function handleClientTimeout(socket, item) {
  log("warn", `Modbus 请求超时（timeout事件）`);

  // 通知前端掉线并将状态置为0
  handleNetworkDisconnectionNotification(item);

  if (currentSocket && currentSocket.connected) {
    currentSocket.emit("plcStatus", {
      status: "timeout",
      ip: item.ip,
      port: item.port,
      timestamp: new Date().toISOString(),
    });
  }
  const content = `${item.ip}:${item.port} 机器人请求超时`;
  setSocket(socket, title, content, item.ip, "timeout", eventName).catch(
    (err) => {
      log("error", "发送socket消息失败:", err);
    }
  );
}

/**
 * 处理网络断开连接通知
 * @param {object} item - PLC设备信息
 */
function handleNetworkDisconnectionNotification(item) {
  try {
    // 将所有状态置为0
    resetAllStatusToZero();

    // 通知前端所有状态已重置
    if (globalSocketInstance) {
      globalSocketInstance.emit("modBusStatus", {
        message: "网络断开，机器人状态已重置",
        inputData: new Array(200).fill(0),
        disconnected: true,
        ip: item.ip,
        port: item.port,
      });
    }

    log("info", `已通知前端网络断开，所有状态已重置为0`);
  } catch (error) {
    log("error", `处理网络断开通知时出错: ${error.message}`);
  }
}

/**
 * 将所有状态重置为0
 */
function resetAllStatusToZero() {
  try {
    console.log("重置状态");
    // 重置 aryModbusStatusList
    const zeroArray = new Array(200).fill(0);
    aryModbusStatusList = [];
    for (let j = 0; j < zeroArray.length; j += 25) {
      const status = zeroArray.slice(j, j + 25);
      aryModbusStatusList.push(status);
    }

    log("info", "所有状态已重置为0");
  } catch (error) {
    log("error", `重置状态时出错: ${error.message}`);
  }
}

/**
 * 检查 aryModbusStatusList 中的值是否全为 0
 * @returns {boolean} 如果所有组中存在不全为 0 的情况，返回 true；否则返回 false
 */
function checkModbusStatusList() {
  aryModbusStatusList = [];

  for (let value = 0; value < 2; value++) {
    const {
      robotStatus,
      robotPosition,
      robotPhotoStatus,
      robotAllFinished,
      robotOneFinished,
    } = modbusStatusHandle(value);
    const ary = [
      robotStatus,
      robotPosition,
      robotPhotoStatus,
      robotAllFinished,
      robotOneFinished,
    ];
    aryModbusStatusList.push(ary);
  }
  // 检查 aryModbusStatusList 是否为空
  if (!aryModbusStatusList || aryModbusStatusList.length === 0) {
    return false;
  }

  // 遍历每个组
  for (let i = 0; i < aryModbusStatusList.length; i++) {
    const group = aryModbusStatusList[i];

    // 检查组是否存在
    if (!group || group.length === 0) {
      continue;
    }

    // 检查组中是否有非零值
    const hasNonZeroValue = group.some((value) => value !== 0);

    // 如果有任何一组不全为 0，则返回 true
    if (hasNonZeroValue) {
      return true;
    }
  }

  // 所有组都全为 0
  return false;
}

/**
 * 启动定时器检查 aryModbusStatusList 的值
 * @param {number} interval - 检查间隔（毫秒）
 * @param {function} onStopCallback - 定时器停止时的回调函数
 * @returns {Promise<boolean>} Promise在定时器停止时resolve为true
 */
function startCheckingModbusStatus(interval = 1000, onStopCallback = null) {
  // 如果定时器已经存在，先清除它
  if (checkModbusStatusTimer) {
    clearInterval(checkModbusStatusTimer);
  }

  // log("info", "启动定时器检查 aryModbusStatusList 中的值");

  // 创建并返回一个Promise，当定时器停止时resolve
  return new Promise((resolve) => {
    // 启动新的定时器
    checkModbusStatusTimer = setInterval(() => {
      const hasNonZeroValues = checkModbusStatusList();

      if (hasNonZeroValues) {
        //  log("info", "aryModbusStatusList 中存在不全为 0 的组，停止定时器");
        clearInterval(checkModbusStatusTimer);
        checkModbusStatusTimer = null;

        // 通知定时器已停止
        if (onStopCallback && typeof onStopCallback === "function") {
          onStopCallback();
        }

        // 解析Promise
        resolve(true);
      } else {
        //  log("info", "aryModbusStatusList 中所有组都全为 0，继续检查");
      }
    }, interval);
  });
}

/**
 * 停止检查 aryModbusStatusList 的定时器
 * @param {function} onStopCallback - 定时器停止时的回调函数
 */
function stopCheckingModbusStatus(onStopCallback = null) {
  if (checkModbusStatusTimer) {
    clearInterval(checkModbusStatusTimer);
    checkModbusStatusTimer = null;
    log("info", "已停止检查 aryModbusStatusList 的定时器");

    // 通知定时器已停止
    if (onStopCallback && typeof onStopCallback === "function") {
      onStopCallback();
    }
  }
}

/**
 * 检查定时器是否正在运行
 * @returns {boolean} 如果定时器正在运行则返回true，否则返回false
 */
function isCheckingModbusStatusRunning() {
  return checkModbusStatusTimer !== null;
}

/**
 * 处理网络重新连接后的业务逻辑
 * @param {*} socket - WebSocket 实例
 * @param {*} item - 当前连接的设备信息
 * @param {*} client - Modbus 客户端实例
 */
async function handleNetworkReconnection(socket, item, client) {
  try {
    log("info", `开始处理网络重新连接后的业务逻辑: ${item.ip}:${item.port}`);

    // 发送通知到前端
    const reconnectionPayload = {
      type: "networkReconnected",
      message: "网络连接已恢复，正在进行重新初始化",
      timestamp: new Date().toISOString(),
      ip: item.ip,
      port: item.port,
    };

    if (socket && socket.connected) {
      socket.emit("plcReconnected", reconnectionPayload);
    }

    // 重新同步PLC状态
    startCheckingModbusStatus(1000, async () => {
      // console.log("定时器已停止");
      const plcStatusData = await getPlcStatus();
      const wirtToPlcData = await getWritingConfig();
      // console.log("获取plcStatusData 的状态", plcStatusData);
      // console.log("获取要写入到PLC的数据", wirtToPlcData);
      handleNetworkDisconnection(
        socket,
        item,
        client,
        plcStatusData,
        wirtToPlcData
      );
    });

    log("info", `网络重新连接后的业务处理完成: ${item.ip}:${item.port}`);
  } catch (error) {
    log("error", `处理网络重新连接业务逻辑时出错: ${error.message}`);
  }
}

// 对网络断线后的数据进行处理
async function handleNetworkDisconnection(
  socket,
  item,
  client,
  plcStatusData,
  wirtToPlcData
) {
  const cameraCount = await getCameraCount();
  for (let index = 0; index < plcStatusData.length; index++) {
    const {
      robotStatus,
      robotPosition,
      robotPhotoStatus,
      robotAllFinished,
      robotOneFinished,
    } = modbusStatusHandle(index);
    // 是拍照状态，但是没有给和机器人通信那么就重新启动相机
    if (
      robotStatus == 1 &&
      robotPosition == 2 &&
      robotPhotoStatus == 1 &&
      robotOneFinished != 1 &&
      cameraCount[index] &&
      cameraCount[index].flag
    ) {
      cameraCount[index].flag = false;
    }
  }
}

/**
 * 到位信号监听
 * @param {*} req - 请求对象
 * @param {*} sn - 序列号
 * @param {*} userId - 用户ID
 * @param {*} socket - WebSocket 实例
 * @param {*} proData - 产品数据
 * @param {*} ipPlcData - PLC IP 数据
 * @param {*} cameraData - 相机数据
 * @param {*} cameraClient - 相机客户端
 */
async function plcArrivalSignal(
  req,
  sn,
  userId,
  socket,
  proData,
  ipPlcData,
  cameraData,
  cameraClient
) {
  // 参数验证
  if (!proData) {
    log("error", "plcArrivalSignal: proData 参数为空");
    return;
  }

  if (!cameraClient) {
    log("error", "plcArrivalSignal: cameraClient 参数为空");
    return;
  }

  log("info", "plcArrivalSignal start ==== plc 到位信号监听开始");
  log(
    "info",
    `参数: sn=${sn}, userId=${userId}, proData=${JSON.stringify(
      proData
    )}, ipPlcData=${JSON.stringify(ipPlcData)}`
  );

  // 检查索引有效性
  const index = (proData.beUsed || 1) - 1;
  if (index < 0 || index >= 4) {
    log("error", `plcArrivalSignal: 无效的beUsed值 ${proData.beUsed}`);
    return;
  }

  // 获取状态
  const {
    robotStatus,
    robotPosition,
    robotPhotoStatus,
    robotAllFinished,
    robotOneFinished,
  } = modbusStatusHandle(index);

  log(
    "info",
    `获取状态: robotStatus=${robotStatus}, robotPosition=${robotPosition}, robotPhotoStatus=${robotPhotoStatus}, robotAllFinished=${robotAllFinished}, robotOneFinished=${robotOneFinished}`
  );

  if (robotStatus == 1 && robotPosition == 1) {
    // 写入商品数据
    writeProData(proData, cameraClient, req);
  }
}

module.exports = {
  modBusSocketInfo,
  plcArrivalSignal,
};
